package com.heima.search.service;


import com.fasterxml.jackson.core.type.TypeReference;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.threadlocal.UserThreadLocalUtils;
import com.heima.common.util.JsonUtils;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.search.dtos.UserSearchDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.redisson.misc.Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.validation.constraints.NotBlank;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;


@Slf4j
@Service
public class UserSearchServiceImpl {
    @Autowired
    private RestHighLevelClient highLevelClient;
    @Autowired
    private RedisService redisService;

    private String indexName = "app_info_article";


    /**
     * 批量创建索引文档
     * @param articleDtoList
     */
    public void importArticle(List<ArticleDto> articleDtoList) {
//        创建批量操作对象
        BulkRequest bulkRequest = new BulkRequest();
//      循环 文章对象
        for (ArticleDto articleDto : articleDtoList) {
//            构造创建文档请求对象
            IndexRequest indexRequest = new IndexRequest(indexName).
                    id(articleDto.getId().toString());
            Map<String,Object> map = new HashMap<>();
            map.put("id",articleDto.getId());
            map.put("publishTime",articleDto.getPublishTime());
            map.put("layout",articleDto.getLayout());
            map.put("images",articleDto.getImages());
            map.put("staticUrl",articleDto.getStaticUrl());
            map.put("channelId",articleDto.getChannelId());
            map.put("authorId",articleDto.getAuthorId());
            map.put("authorName",articleDto.getAuthorName());
            map.put("labels",articleDto.getLabels());
            map.put("title",articleDto.getTitle());
            List<Map<String,Object>> content = JsonUtils.nativeRead(
                    articleDto.getContent(),
                    new TypeReference<List<Map<String, Object>>>() {});
            String contentText = "";
            if(!CollectionUtils.isEmpty(content)){
                for (Map<String, Object> map1 : content) {
                    String type = map1.get("type").toString();
                    if("text".equals(type)){
                        String value = map1.get("value").toString();
                        if(contentText.length()>0){
                            contentText += ",";
                        }
                        contentText += value;
                    }
                }
            }
            map.put("content",contentText);
//            把map转json
            String json = JsonUtils.toString(map);
            indexRequest.source(json, XContentType.JSON);
            bulkRequest.add(indexRequest);
        }
        try {
            highLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            log.error("db数据导入Es异常！！");
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    /**
     * 关键词搜索
     * @param dto
     * @return
     */
    public List<ArticleDto> search(UserSearchDto dto) {
//        用户输入的关键词
        String searchWords = dto.getSearchWords();
//        创建搜索请求封装对象
        SearchRequest searchRequest = new SearchRequest(indexName);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//        构造bool查询
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
//        设置搜索条件
        if(StringUtils.isBlank(searchWords)){
            boolQuery.must(QueryBuilders.matchAllQuery());
        }else{
            boolQuery.must(QueryBuilders.matchQuery("title",searchWords).operator(Operator.AND));
        }
        if(dto.getMinBehotTime() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime()));
        }
        sourceBuilder.query(boolQuery);
//        设置分页
        int index = dto.getFromIndex();
        sourceBuilder.from(index);
        sourceBuilder.size(dto.getPageSize());
//        设置高亮
        if(StringUtils.isNotBlank(searchWords)) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
            highlightBuilder.field("title");
            highlightBuilder.postTags("</font>");
            sourceBuilder.highlighter(highlightBuilder);
        }



        sourceBuilder.sort("publishTime", SortOrder.DESC);


        searchRequest.source(sourceBuilder);
//        发送给Es服务端
        List<ArticleDto> articleDtoList = new ArrayList<>();
        try {
            SearchResponse response = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//        解析Es的返回数据
            SearchHits hits = response.getHits();
            for (SearchHit hit : hits) {
                String json = hit.getSourceAsString();
                ArticleDto articleDto = JsonUtils.toBean(json, ArticleDto.class);

                if(StringUtils.isNotBlank(searchWords)) {
//                获取高亮结果
                    Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                    HighlightField field = highlightFields.get("title");
                    Text[] texts = field.getFragments();
                    String title = StringUtils.join(texts);
//                高亮后的标题
                    articleDto.setTitle(title);
                }
                articleDtoList.add(articleDto);
            }
//        构造返回结果
        } catch (IOException e) {
            log.error("关键词搜索异常");
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }

        if(!CollectionUtils.isEmpty(articleDtoList)){
//            从Threadlocal中获取用户id
            Integer userId = UserThreadLocalUtils.getUserId();
//            搜索到数据,保存搜索记录
            log.info("=====线程id={}"+Thread.currentThread().getId());
            redisService.saveUserWord(dto,userId);
        }

        return articleDtoList;

    }
}
